﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Globalization;

namespace Dwarf.Managed
{
    class Settings
    {

        /// <summary>
        /// Short dwarf's description.
        /// </summary>
        private const String DWARF_NAME = "Graph Models, managed hybrid kernel.";

        /// <summary>
        /// Constants for output files's names
        /// </summary>
        private const String DWARF_NAME_OUTPUT = "Dwarf.Managed.Hybrid";
        private const String PROFILE_TXT = ".Profile.txt";
        private const String RESULT_TXT = ".Result.txt";
        public const int ROOT_PROCESS = 0;
        /// <summary>
        /// Default size for buffer.
        /// </summary>
        private const int BUFFER_SIZE = 1024;

        /// <summary>
        /// Input & output files.
        /// </summary>
        private String inputFile;
        private String profileFile;
        private String resultFile;

        public Settings()
        {
            inputFile = new String(new char[BUFFER_SIZE]);
            profileFile = new String(new char[BUFFER_SIZE]);
            resultFile = new String(new char[BUFFER_SIZE]);
        }

        /// <summary>
        /// Start the time count.
        /// </summary>
        public void Start()
        {
            start();
        }

        /// <summary>
        /// Problem results output.
        /// </summary>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        public void Finish(Solver solver)
        {
            double time = 0; ;
            stop(ref time);

            Console.WriteLine("Clock time (sec): {0} ", time.ToString("F8"));

            this.WriteResults(solver,time);
        }

        /// <summary>
        /// Init variables, parse cli params and input file.
        /// </summary>
        /// <param name="args">
        /// Cli params.
        /// </param>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        public void Init(String[] args, Solver solver)
        {
            //Parse cli params.
            parseArguments(
                    args.Length,
                    args,
                    ref inputFile,
                    ref profileFile,
                    ref resultFile);

            //Parse input file.
            this.parseConfigFile(solver);

            // write settings if it's needed
            this.WriteSettings(solver);
        }

        private void WriteResults(Solver solver, double time)
        {
                try
                {
                    using (StreamWriter outputResult = File.CreateText(resultFile))
                    {
                        using (StreamWriter outputProfile = File.CreateText(profileFile))
                        {
                            outputProfile.WriteLine("#Dwarf name:{0}", DWARF_NAME);
                            outputProfile.WriteLine("#Time: {0}", DateTime.Now.ToString());
                            outputProfile.WriteLine("#Result time (sec): {0} ", time);
                            outputResult.WriteLine("#Result domain:");
                            int length = solver.vit.getLengthOfObservationSequence();
                            for (int i = 0; i < length; i++)
                            {
                                outputResult.Write("{0} ", solver.vit.sequenceOfState[i]);
                            }
                            outputResult.WriteLine();
                            outputResult.WriteLine("Probability: {0} ", solver.vit.getProbability());
                            outputProfile.WriteLine("#eof");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw new Exception("Error in output file");
                }
            
        }

        /// <summary>
        /// Parse input file into solver's structures.
        /// </summary>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        private void parseConfigFile(Solver solver)
        {
            // Open the file to read from.
            try
            {
                using (StreamReader sr = File.OpenText(inputFile))
                {
                    int m, n, t;
                    //temp string for parsing file
                    String str;
                    //temp array of string for splitting 
                    String[] tmpStrArray;

                    // read the number of observation symbols
                    str = sr.ReadLine();
                    tmpStrArray = str.Split(':');
                    m = Convert.ToInt32(tmpStrArray[1]);

                    // read the number of states
                    str = sr.ReadLine();
                    tmpStrArray = str.Split(':');
                    n = Convert.ToInt32(tmpStrArray[1]);

                    solver.hmm = new HMM(n, m);
                    // read the state transition matrix
                    sr.ReadLine();
                    for (int i = 0; i < n; i++)
                    {
                        str = sr.ReadLine();
                        tmpStrArray = str.Split(' ');
                        for (int j = 0; j < n; j++)
                        {
                            solver.hmm.stateTransitionMatrix[i][j] = double.Parse(tmpStrArray[j], CultureInfo.InvariantCulture);
                        }
                    }
                    // read the observation probability matrix
                    sr.ReadLine();
                    for (int i = 0; i < n; i++)
                    {
                        str = sr.ReadLine();
                        tmpStrArray = str.Split(' ');
                        for (int j = 0; j < m; j++)
                        {
                            solver.hmm.observationProbabilityMatrix[i][j] = double.Parse(tmpStrArray[j], CultureInfo.InvariantCulture);
                        }
                    }
                    // read initial states
                    sr.ReadLine();
                    str = sr.ReadLine();
                    tmpStrArray = str.Split(' ');
                    for (int i = 0; i < n; i++)
                    {
                        solver.hmm.initialStates[i] = double.Parse(tmpStrArray[i], CultureInfo.InvariantCulture);
                    }

                    // read the length of observation sequence
                    str = sr.ReadLine();
                    tmpStrArray = str.Split(':');
                    t = Convert.ToInt32(tmpStrArray[1]);

                    solver.vit = new ViterbiModel(t, solver.hmm);

                    sr.ReadLine();

                    //read the observation sequence
                    str = sr.ReadLine();
                    tmpStrArray = str.Split(' ');
                    for (int i = 0; i < t; i++)
                    {
                        solver.vit.observationSequence[i] = Convert.ToInt32(tmpStrArray[i]);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                Console.WriteLine(ex.StackTrace);
                throw new Exception("Error in input file");
            }
        }

        /// <summary>
        /// Write settings.
        /// </summary>
        /// <param name="solver">
        /// Instance of Solver.
        /// </param>
        private void WriteSettings(Solver solver)
        {
            // Write general settings.
            Console.WriteLine("Kernel settings summary : ");
            Console.WriteLine("Dwarf name              : {0} ", DWARF_NAME);
            Console.WriteLine("Inputfile               : {0} ", inputFile);
            Console.WriteLine("Profilefile             : {0} ", profileFile);
            Console.WriteLine("Resultfile              : {0} ", resultFile);
            Console.WriteLine();
        }

        /// <summary>Parse the command line arguments and fill the reference parameters</summary>
        /// <param name="argc">Count of command line tokens</param>
        /// <param name="argv">Command line tokens</param>
        /// <param name="log">Settings log (debug/benchmark)</param>
        /// <param name="inputFile">Name of input text file</param>
        /// <param name="outputFile">Name of output file</param>
        [DllImport(@"CliTools.dll")]
        private static extern void parseArguments(
            int argc,
            String[] argv,
            ref string inputFile,
            ref string profileFile,
            ref string resultFile
            );

        [DllImport(@"CliTools.dll")]
        private static extern void start();

        [DllImport(@"CliTools.dll")]
        private static extern void stop(ref double time);
    }
}